﻿using System;
using System.Windows;

namespace SStuff.FlowControls
{
    /// <summary>
    /// Layout algorithm for displaying a Coverflow (with 3D translation, 3D rotation and opacity)
    /// </summary>
    public class CoverFlow3D : IFlowLayout3DAlgorithm
    {
        Size _availableSize;

        #region IFlowLayout3DAlgorithm Members

        public void SetAvailableSize(Size availableSize)
        {
            _availableSize = availableSize;
        }

        public int GetMaxDisplayedItems()
        {
#if PHONE
            return 7;
#else
            return 11;
#endif
        }

        public void ComputeLayout(double normalizedOffset, IFlowItem3D item, int consideredItemsCount)
        {
            // calculate the offset difference between each item
            double step = 1.0 / consideredItemsCount;

            // calculate the absolute offset
            var absOffset = Math.Abs(normalizedOffset);

            
            if (absOffset < step)
            {
                // layouting for the positions next to the center

                // calculate the "close offset" (offset from the center to the next stable position)
                var closeOffset = normalizedOffset * consideredItemsCount;

                // Translation on X range is [(-0.15) * _availableSize.Width - (+0.15) * _availableSize.Width]
                item.Transform3D.GlobalOffsetX = closeOffset * 0.15 * _availableSize.Width;

                // Translation on Z range is [0 - -250]
                item.Transform3D.GlobalOffsetZ = -Math.Abs(closeOffset) * 250;

                // rotation range is [-60 - +60]
                item.Transform3D.RotationY = closeOffset * 60;
                item.Opacity = 1;
            }
            else
            {
                // layouting for positions far from center

                // First approximation of offset X (+/- 0.15 * _availableSize.Width) to meet limits of the close layout
                item.Transform3D.GlobalOffsetX = normalizedOffset > 0 ? 0.15 * _availableSize.Width : -0.15 * _availableSize.Width;


                // First approximation of offset Z (-250) to meet limits of the close layout
                item.Transform3D.GlobalOffsetZ = -250;
                // compute an offset that eliminate the close offset range
                var closeOffset = normalizedOffset>0? normalizedOffset - step:normalizedOffset+step;

                // compute the additional offset X and Z
                item.Transform3D.GlobalOffsetX += 1.35 * closeOffset * _availableSize.Width;
                item.Transform3D.GlobalOffsetZ -= 2000 * Math.Abs(closeOffset);

                // rotation does not change (it's the limit of the close layout). The Perspective angle produced by the translation will produce the perspective effect for us
                item.Transform3D.RotationY = normalizedOffset > 0 ? 60 : -60;

                // if we are very far from the center, we apply an opacity effect
                if (1 - absOffset <= step)
                {
                    var tailOffset = (1 - absOffset) * consideredItemsCount;
                    if (tailOffset < 0)
                        item.Opacity = 0;
                    else
                        item.Opacity = tailOffset;
                }
                else
                {
                    item.Opacity = 1;
                }
            }
        }

        public bool SupportsGestures
        {
            get { return true; }
        }

        public double GetNormalizedOffsetAtPoint(Point contactPosition)
        {
            // Hit testing is done trough a linear evaluation (wich is not exact but that produce a smoother result)
            double halfWidth = 0.5 * _availableSize.Width;
            double x = contactPosition.X - halfWidth;
            return x / halfWidth;
        }

        /// <summary>
        /// Raised when the algorithm want to invalidate the current layout
        /// </summary>
        public event EventHandler LayoutChanged;

        #endregion
    }
}
